import { IEventDispatcher, EventHandler, IValid, IAppEvent } from "../interfaces";
import { AppEvent } from "./AppEvent";

export class EventDispatcher implements IEventDispatcher {


    private eventHash: { [key: string]: IAppEvent[] } = {};

    //用于处理在emit时事件删除自己的情况。
    protected isEmiting: { [key: string]: boolean } = {}
    protected offEvents: { [key: string]: IAppEvent[] } = {};

    offAll() {
        this.eventHash = {};
    }

    offByName(type: string) {
        this.eventHash[type] = [];
    }

    clearByTarget(type: string, ower: any) {
        var events = this.eventHash[type];
        if (events && events.length > 0) {
            for (let index = 0; index < events.length; index++) {
                const event = events[index];
                if (event.target === ower) {
                    events.splice(index, 1)
                    break;
                }
            }
        }
    }
    on(event: IAppEvent): void
    on(topic: string, handler: EventHandler, target: IValid, isBlock?: boolean): void
    on(param: any, handler?: EventHandler, target?: IValid, isBlock?: boolean): void {
        let topic: any;
        if (!handler) {
            topic = param.topic;
            handler = param.handler
            target = param.target;
        } else {
            topic = param
        }
        let index = this.has(topic, handler, target)
        if (index >= 0) {
            return;
        }
        if (!this.eventHash[topic]) {
            this.eventHash[topic] = [];
        }
        var event: AppEvent;
        if (!handler) {
            event = param;
        } else {
            event = new AppEvent(topic, handler, target, isBlock);
        }

        this.eventHash[topic].push(event);
    }

    has(topbic: string, handler: EventHandler, target: IValid) {
        var events = this.eventHash[topbic];
        if (events && events.length > 0) {
            for (let index = 0; index < events.length; index++) {
                const event = events[index];
                if (event.target === target && event.handler === handler) {
                    return index;
                }
            }
        }
        return -1;
    }

    private pushToOffEvents(event: IAppEvent) {
        let list = this.offEvents[event.topic]
        if (!list) {
            list = []
            this.offEvents[event.topic] = list;
        }
        list.push(event)
    }

    off(event: IAppEvent): void
    off(topic: string, handler: EventHandler, target: IValid): void
    off(param: any, handler?: EventHandler, target?: IValid): void {
        let topic: string;
        if (handler) {
            topic = param;
            var events = this.eventHash[topic];
            if (events && events.length > 0) {
                for (let index = 0; index < events.length; index++) {
                    const event = events[index];
                    if (event.target === target && event.handler === handler) {
                        if (this.isEmiting[topic]) {
                            this.pushToOffEvents(event)
                        } else {
                            events.splice(index, 1);
                        }
                        break;
                    }
                }
            }
        } else {
            topic = param.topbic;
            var events = this.eventHash[topic];
            if (events && events.length > 0) {
                for (let index = 0; index < events.length; index++) {
                    const event = events[index];
                    if (event === param) {
                        if (this.isEmiting[topic]) {
                            this.pushToOffEvents(event)
                        } else {
                            events.splice(index, 1);
                        }
                        break;
                    }
                }
            }
        }
        var events = this.eventHash[topic];

    }

    private remove(event: IAppEvent) {
        let type: string = event.topic
        var events = this.eventHash[type];
        if (events) {
            let index = events.indexOf(event)
            if (index >= 0) {
                events.splice(index, 1)
            }
        }
    }

    emit(topic: string, data: any): void {
        var events = this.eventHash[topic];
        if (events && events.length > 0) {
            this.isEmiting[topic] = true;
            for (let index = 0; index < events.length; index++) {
                const event: IAppEvent = events[index];
                if (event.target.isValid) {
                    if (event.target.isValid()) {
                        event.handler.call(event.target, data)
                        if (event.isBlock) {
                            break;
                        }
                    } else {
                        this.pushToOffEvents(event)
                    }
                } else {
                    event.handler.call(event.target, data)
                }
            }
            this.isEmiting[topic] = false;

            let list = this.offEvents[topic]
            if (list) {
                while (list.length > 0) {
                    let delTarget: IAppEvent = list.shift()
                    this.remove(delTarget)
                }
            }
        }
    }
}





